from typing import Any
import numpy as np


class EarlyStopping(object):
    def __init__(self, patience=15, delta:float=0., autodelta = False) -> None:
        """
        Tp control early stopping
        Args:
            patience: int, number of epochs to wait before stopping
            delta: float, the minimum improvements
            autodelta: bool, auto define the minimum improvements
        """
        self.patience = patience
        self.delta = delta
        self.autodelta = autodelta
        self.counter = 0
        self.early_stop = False
        self.best_loss = float('inf')
        self.history_loss = []

    def __call__(self, loss:float|list[float], k = 0.01) -> Any:
        loss_value = np.sum(loss) / len(loss) if type(loss) == list else loss
        self.history_loss.append(loss_value)
        if not self.autodelta:
            # 采用自适应delta
            if self.history_loss == []:
                self.delta = float('inf')
            elif len(self.history_loss) < self.patience:
                self.delta = np.sum(self.history_loss) / self.patience * k
            else:
                self.delta = np.sum(self.history_loss[-self.patience:]) / self.patience * k
        if loss_value < self.best_loss: # type: ignore
            # 当损失未大于历史最优损失时
            self.best_loss = loss_value
            self.counter = 0
        elif loss_value >= self.best_loss + self.delta: # type: ignore
                # 当损失大于历史最优损失时
                self.counter += 1
                if self.counter > self.patience:
                    self.early_stop = True